using System;

namespace RapidHDL
{
	/// <summary>
	/// Summary description for Structure.
	/// </summary>
	public class Structure
	{
		public SharedDataCoordinator ComponentSharedDataCoordinator;
		public TruthTableCoordinator TruthTableCoordinator;
		public RapidHardware RapidHardware;
		public TopLevelComponent TopLevelComponent;
        public ExternalInterfaceComponent ExternalInterface;

        public bool FileChanged; 

		// private int iNextSourcePathId; remove warning

		private System.Collections.Hashtable oTestedComponents;

		public Structure(RapidHardware poRapidHardware)
		{
			RapidHardware = poRapidHardware;
			TruthTableCoordinator = new TruthTableCoordinator();
			TopLevelComponent = new TopLevelComponent(poRapidHardware);
			ComponentSharedDataCoordinator = new SharedDataCoordinator();
            ExternalInterface = new ExternalInterfaceComponent(TopLevelComponent);
			//iNextSourcePathId = 0;
            FileChanged = true;
		}

		public bool GenerateStructure()
		{
			TopLevelComponent.BaseGenerateStructure();
            return true;
		}

        public bool GenerateExternalInterface()
        {
            ExternalInterface.GenerateExternalSignalInterface();

            if (!ExternalInterface.SortStructure())
                return false;
            if (!ExternalInterface.ValidateWellFormedComponent(!RapidHardware.Settings.DisableCheckWellFormed))
                return false;

            return true;
        }

        public bool StartSimulation()
        {
            if (!TopLevelComponent.SortStructure())
                return false;
            if (!TopLevelComponent.ValidateWellFormedComponent(!RapidHardware.Settings.DisableCheckWellFormed))
                return false;
            return true;
        }

        public string TestComponent(string psTestName, string psComponentPath)
        {
            return TestComponent(psTestName, psComponentPath, false);
        }

        public string TestComponent(string psTestName, string psComponentPath, bool pbTestChildren)
        {
            Component oComponet = GetComponent(psComponentPath);
            if (oComponet == null)
                return "Component " + psComponentPath + " failed.  Component not found.";

            oTestedComponents = new System.Collections.Hashtable();
            return TestComponent(psTestName,oComponet,oComponet.ParentComponent.HirarchicalName,pbTestChildren);

        }

		public string TestComponent(string psTestName, Component poComponent, string psComponentNameHirarchy, bool pbTestChildren)
		{
			RapidHardware oRapidHardware = new RapidHardware();

			string sTestResult;
			string sType;
			string sComponentNameHirarchy;

			oRapidHardware.InternalLog.EnableDisplayToConsole = true;
			oRapidHardware.InternalLog.DisplayLogs.Add(LogType.SimulationTrace);

			sType = poComponent.GetType().ToString();
			if (oTestedComponents.ContainsKey(sType))
				return "";

        //public bool TestComponent(string psTestName, string psComponentNameHirarchy)
 
			sComponentNameHirarchy = psComponentNameHirarchy + "." + poComponent.Name;
			
			oRapidHardware.InternalLog.Log(LogType.SimulationTrace, "");
			oRapidHardware.InternalLog.Log(LogType.SimulationTrace,"***** Starting Test of " + sType + " ********");

			sTestResult = poComponent.BaseTestComponent(oRapidHardware,psTestName);

			oRapidHardware.InternalLog.Log(LogType.SimulationTrace,"====== Finished Test of " + sType + " ========");
			oRapidHardware.InternalLog.Log(LogType.SimulationTrace,"");


			if (sTestResult != "")
				return "Component of type '" + sType + "' failed test '" + psTestName + "' and returned '" + sTestResult + "'.  {" + sComponentNameHirarchy + "}";

            if (!pbTestChildren)
                return "";

			foreach(Component oComponent in poComponent.Components.Values)
			{
				string result = TestComponent(psTestName,oComponent,sComponentNameHirarchy,true);
                if (result != "")
                    return result;
			}

            return "";
		}

		public string TestAll(string psTestName)
		{
            string sResult = "";
            string sFinal = "";
			string sComponentNameHirarchy = "TopLevelComponent";
			System.Diagnostics.Debug.WriteLine("********** Starting TestAll " + psTestName + "  **************");
			oTestedComponents = new System.Collections.Hashtable();
			
			foreach(Component oComponent in TopLevelComponent.Components.Values)
			{
				sResult = TestComponent(psTestName,oComponent,sComponentNameHirarchy,true);
                sFinal += sResult + "\n";
			}
			System.Diagnostics.Debug.WriteLine("********** Finished TestAll " + psTestName + "  **************");
            return sFinal;
		}

		private bool TransformStructureToVerilog()
		{

            //add a new top level component...
            //ExternalInterfaceComponent ExternalInterface = new ExternalInterfaceComponent(TopLevelComponent,16,12,16);
            
			//TopLevelComponent.PrepareVerilogStructure();
			//TopLevelComponent.BaseTransformStructureToVerilog();

            FileChanged = false;

            ExternalInterface.PrepareVerilogStructure();

            ExternalInterface.BaseTransformStructureToVerilog();

            TopLevelComponent.WriteTopVerilog();

            return true;
		}

        public bool CopyFromTemp()
        {
            string sPath1 = RapidHardware.Settings.OutputPath + @"temp\";
            string sPath2 = RapidHardware.Settings.OutputPath;

            string[] saFiles = System.IO.Directory.GetFiles(sPath1);
            bool bFileCopied = false;
            foreach (string sFile in saFiles)
            {
                string sFileName = Utility.StringTakeRightFromEnd(sFile, '\\');
                if (!TextFile.CheckFilesMatch(sPath1, sFileName, sPath2, sFileName, 1))
                {
                    bFileCopied = true;
                    System.IO.File.Copy(sPath1 + sFileName, sPath2 + sFileName,true);
                }
            }
            FileChanged = bFileCopied;
            return true;
        }

        public bool SimulateHardwareSource()
        {
            RapidHardware.Transcript.AddTranscript("Verilog Simulation Started");

            string sResult = RapidHardware.HardwareScripts.SimulateVerilogTestBenches();
            if (sResult != "")
            {
                RapidHardware.Transcript.AddTranscript(sResult);
                RapidHardware.Transcript.AddTranscript("Verilog Simulation Failed");
                return false;
            }

            RapidHardware.Transcript.AddTranscript("Verilog Simulation Complete");
            return true;
        }

        public bool SimulateSynthesisSource()
        {
            RapidHardware.Transcript.AddTranscript("Verilog Post-Synthesis Simulation Started");

            string sResult = RapidHardware.HardwareScripts.SimulateSynthesisVerilogTestBenches();
            if (sResult != "")
            {
                RapidHardware.Transcript.AddTranscript(sResult);
                RapidHardware.Transcript.AddTranscript("Verilog Post-Synthesis Simulation Failed");
                return false;
            }

            RapidHardware.Transcript.AddTranscript("Verilog Post-Synthesis Simulation Complete");
            return true;
        }


        public bool BuildHardwareSource()
        {
            string sResult = "";
			
            if (!System.IO.Directory.Exists(RapidHardware.Settings.OutputPath))
            {
                try
                {
                    System.IO.Directory.CreateDirectory(RapidHardware.Settings.OutputPath);
                }
                catch
                {
                    RapidHardware.Transcript.AddTranscript("Unable to create folder " + RapidHardware.Settings.OutputPath);
                    return false;
                }
            }

            if (!System.IO.Directory.Exists(RapidHardware.Settings.OutputPath + "temp\\"))
            {
                try
                {
                    System.IO.Directory.CreateDirectory(RapidHardware.Settings.OutputPath + "temp\\");
                }
                catch
                {
                    RapidHardware.Transcript.AddTranscript("Unable to create folder " + RapidHardware.Settings.OutputPath + "temp\\");
                    return false;
                }
            }

            TextFile.WipeDirectory(RapidHardware.Settings.OutputPath + "temp\\","*");

            try
            {
                XilinxTools.StartXSTScript(RapidHardware.Settings.OutputPath);
            }
            catch
            {
                RapidHardware.Transcript.AddTranscript("Error starting XST script in folder " + RapidHardware.Settings.OutputPath);
                return false;
            }

            sResult = XilinxTools.CopyInterfaceFiles(RapidHardware.Settings.InterfaceSourcePath, RapidHardware.Settings.OutputPath);
            if (sResult != "")
            {
                RapidHardware.Transcript.AddTranscript(sResult);
                XilinxTools.CompleteXSTScript();            
                return false;
            }

            TransformStructure(RapidHardware.Settings.OutputFormat);

            RapidHardware.HardwareScripts.WriteVerilogTestBenches();

            if (!CopyFromTemp())
                return false;

            XilinxTools.CompleteXSTScript();


            RapidHardware.Transcript.AddTranscript("Hardware Generation Complete");
            return true;
        }


        public bool BuildHardware(bool pbPrompt)
        {
            try
            {
                bool bBuildLocal = true;
                bool bPromptWasYes = true;

                if (!FileChanged && pbPrompt)
                    if (System.Windows.Forms.MessageBox.Show("Rebuild Hardware?", "Source Files Unchanged", System.Windows.Forms.MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.No)
                        bPromptWasYes = false;

                if (FileChanged || bPromptWasYes)
                {
                    if (RapidHardware.Settings.BuildRemote)
                    {
                        bBuildLocal = false;
                        RapidHardware.Transcript.AddTranscript("Building Remotely");
                        RemoteSynth oRemoteSynth = new RemoteSynth(this);

                        string sBuildErrors = oRemoteSynth.XilinxRemoteBuild();
                        if (sBuildErrors.Length > 0)
                        {
                            RapidHardware.Transcript.AddTranscript("Failed running Remote Synthesis");
                            RapidHardware.Transcript.AddTranscript(sBuildErrors);
                            if (System.Windows.Forms.MessageBox.Show("Remote Build Error: \n" + sBuildErrors + "\n\nTry building locally?", "Remote Build Hardware", System.Windows.Forms.MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.No)
                                return false;
                            bBuildLocal = true;
                        }
                    }

                    if (bBuildLocal)
                    {
                        string sBuildErrors = XilinxTools.LaunchXilinxBuild(RapidHardware.Settings.XilinxToolsPath, RapidHardware.Settings.OutputPath, RapidHardware.Settings.TopComponentName, RapidHardware.Settings.XilinxChip, RapidHardware.Settings.OptimizeSpeed, "");
                        if (sBuildErrors.Length > 0)
                        {
                            RapidHardware.Transcript.AddTranscript("Failed running Synthesis");
                            RapidHardware.Transcript.AddTranscript(sBuildErrors);
                            return false;
                        }
                    }
                    RapidHardware.Transcript.AddTranscript("Synthesis Completed");

                    if (RapidHardware.Settings.RemoteFPGAServer == "")
                    {
                        if (!XilinxTools.UploadBitFile(RapidHardware.Settings.OutputPath, RapidHardware.Settings.TopComponentName))
                            return false;
                    }
                }

                if (RapidHardware.Settings.RemoteFPGAServer != "")
                {
                    if (!FileChanged && pbPrompt && !bPromptWasYes)
                        if (System.Windows.Forms.MessageBox.Show("Load Remote Bitfile?", "Source Files Unchanged", System.Windows.Forms.MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.No)
                            bPromptWasYes = false;
                        else
                            bPromptWasYes = true;

                    if (bPromptWasYes)
                    {
                        string sShortFileName = RapidHardware.Settings.TopComponentName.ToLower() + ".bit";
                        string sFilePath = TextFile.BuildFileName(RapidHardware.Settings.OutputPath, sShortFileName);

                        PicoSvc oPicoSvc = new PicoSvc(RapidHardware.Settings.RemoteFPGAServer);
                        string sResult = oPicoSvc.PrepareBitFile(RapidHardware.Settings.TopComponentName);
                        if (sResult != "OK")
                        {
                            RapidHardware.Transcript.AddTranscript("Failed to upload bitfile to remote FPGA.");
                            RapidHardware.Transcript.AddTranscript(sResult);
                            return false;
                        }

                        RemoteSynth oRemoteSynth = new RemoteSynth(this);
                        oRemoteSynth.PostFile(sFilePath, sShortFileName, RapidHardware.Settings.RemoteFPGAServer);

                        sResult = oPicoSvc.UploadBitFile(RapidHardware.Settings.TopComponentName);
                        if (sResult != "OK")
                        {
                            RapidHardware.Transcript.AddTranscript("Failed to upload bitfile to remote FPGA.");
                            RapidHardware.Transcript.AddTranscript(sResult);
                            return false;
                        }
                    }
                }

                RapidHardware.Transcript.AddTranscript("Bitfile Loaded");

                RapidHardware.FPGAInterface.Start();

                FileChanged = false;
                return true;
            }
            catch (Exception ex)
            {
                RapidHardware.Transcript.AddTranscript("Runtime Error: " + ex.Message);
                return false;
            }
        }

		public void TransformStructure(StructureTransformType peStructureTransformType)
		{
			switch(peStructureTransformType)
			{
				case StructureTransformType.Verilog: 
					TransformStructureToVerilog();
					break;
			}
		}

        public Component GetComponent(string psPath)
        {
            return TopLevelComponent.GetComponent(psPath);
        }

        public NodeVector GetNodeVector(string psPath)
        {
            return TopLevelComponent.GetNodeVector(psPath);
        }

        public bool SynthesizeHardware()
        {
            RapidHardware.Transcript.AddTranscript("Starting ASIC Synthesis");
            TopLevelComponent.WriteLeonardoScript();
            string sErrors = XilinxTools.LaunchSpectrumBuild(RapidHardware.Settings.LeonardoPath, RapidHardware.Settings.OutputPath);
            if (sErrors.Trim().Length > 0)
            {
                RapidHardware.Transcript.AddTranscript(sErrors);
                RapidHardware.Transcript.AddTranscript("ASIC Synthesis Failed");
                return false;
            }
            RapidHardware.Transcript.AddTranscript("Completed ASIC Synthesis");
            return true;
        }

    }
}
